using System;
using CodePlex.TfsLibrary.ObjectModel;
using CodePlex.TfsLibrary.RepositoryWebSvc;
using Xunit;

namespace CodePlex.TfsLibrary.ClientEngine
{
    public class TfsEngineCheckoutTest
    {
        [Fact]
        public void CallsCallbackOnceForEachItem()
        {
            SourceItemResult callbackResult = SourceItemResult.S_Ok;
            TestableTfsEngine engine = TestableTfsEngine.Create();
            engine.SourceControlService.QueryItems__Result = engine.SourceControlService._MultiItem;
            int itemCount = 0;

            engine.Checkout("http://tfsUrl", "$/Foo", @"C:\Foo", true, null,
                            delegate(SourceItem item,
                                     SourceItemResult result)
                            {
                                ++itemCount;
                                callbackResult = result;
                            });

            Assert.Equal(SourceItemResult.S_Ok, callbackResult);
            Assert.Equal(engine.SourceControlService.QueryItems__Result.Length, itemCount);
        }

        [Fact]
        public void CallsQueryItems()
        {
            TestableTfsEngine engine = TestableTfsEngine.Create();
            VersionSpec version = VersionSpec.FromChangeset(42);

            engine.Checkout("http://tfsUrl", "$/Foo", @"C:\Foo", true, version, null);

            Assert.Equal("http://tfsUrl", engine.SourceControlService.QueryItems_TfsUrl);
            Assert.Equal("$/Foo", engine.SourceControlService.QueryItems_ServerPath);
            Assert.Equal(version, engine.SourceControlService.QueryItems_VersionSpec);
            Assert.Equal(RecursionType.Full, engine.SourceControlService.QueryItems_Recursion);
            Assert.Equal(ItemType.Any, engine.SourceControlService.QueryItems_ItemType);
            Assert.Equal(DeletedState.NonDeleted, engine.SourceControlService.QueryItems_DeletedState);
        }

        [Fact]
        public void CallsWebTransferServiceForFileItem()
        {
            TestableTfsEngine engine = TestableTfsEngine.Create();
            SourceItem serverItem = engine.SourceControlService._SingleFile[1];
            string localItemPath = TfsUtil.ServerPathToLocalPath("$/Foo", @"C:\Foo", serverItem.RemoteName);
            engine.SourceControlService.QueryItems__Result = engine.SourceControlService._SingleFile;

            engine.Checkout("http://tfsUrl", "$/Foo", @"C:\Foo", true, null, null);

            Assert.Equal(1, engine.WebTransferService.Download__FilesDownloaded.Count);
            Assert.Equal(localItemPath, engine.WebTransferService.Download__FilesDownloaded[serverItem.DownloadUrl]);
        }

        [Fact]
        public void CheckoutToExistingDirectoryWorks()
        {
            TestableTfsEngine engine = TestableTfsEngine.Create();
            engine.FileSystem.EnsurePath(@"C:\Foo");

            engine.Checkout("http://tfsUrl", "$/Foo", @"C:\Foo", true, null, null);

            SourceItem localItem = engine.GetSourceItem(@"C:\Foo");
            Assert.Equal(SourceItemStatus.Unmodified, localItem.LocalItemStatus);
        }

        [Fact]
        public void DownloadFailure()
        {
            SourceItem callbackItem = null;
            SourceItemResult callbackResult = SourceItemResult.S_Ok;
            TestableTfsEngine engine = TestableTfsEngine.Create();
            engine.SourceControlService.QueryItems__Result = engine.SourceControlService._SingleFile;
            engine.WebTransferService.Download__ThrowsException = true;

            engine.Checkout("http://tfsUrl", "$/Foo", @"C:\Foo", true, VersionSpec.Latest,
                            delegate(SourceItem item,
                                     SourceItemResult result)
                            {
                                callbackItem = item;
                                callbackResult = result;
                            });

            Assert.Equal(@"C:\Foo\file1.txt", callbackItem.LocalName);
            Assert.Equal(SourceItemResult.E_AccessDenied, callbackResult);
            Assert.True(engine.TfsState.IsFolderTracked(@"C:\Foo"));
            Assert.False(engine.TfsState.IsFileTracked(@"C:\Foo\file1.txt"));
        }

        [Fact]
        public void FileFromServerIsTracked()
        {
            TestableTfsEngine engine = TestableTfsEngine.Create();
            engine.SourceControlService.QueryItems__Result = engine.SourceControlService._SingleFile;

            engine.Checkout("http://tfsUrl", "$/Foo", @"C:\Foo", true, null, null);

            SourceItem sourceItem = engine.TfsState.GetSourceItem(@"C:\Foo\file1.txt");
            Assert.Equal(ItemType.File, sourceItem.ItemType);
            Assert.Equal(SourceItemStatus.Unmodified, sourceItem.LocalItemStatus);
            Assert.Equal(engine.SourceControlService._SingleFile[1].RemoteChangesetId, sourceItem.LocalChangesetId);
            Assert.Equal(engine.SourceControlService._SingleFile[1].ItemId, sourceItem.ItemId);
        }

        [Fact]
        public void FolderFromServerIsTracked()
        {
            TestableTfsEngine engine = TestableTfsEngine.Create();
            engine.SourceControlService.QueryItems__Result = engine.SourceControlService._SingleDirectory;

            engine.Checkout("http://tfsUrl", "$/Foo", @"C:\Foo", true, null, null);

            Assert.True(engine.FileSystem.DirectoryExists(@"C:\Foo\folder"));

            SourceItem sourceItem = engine.TfsState.GetSourceItem(@"C:\Foo\folder");
            Assert.Equal(7, sourceItem.LocalChangesetId);
            Assert.Equal(3, sourceItem.ItemId);
            Assert.Equal(ItemType.Folder, sourceItem.ItemType);
            Assert.Equal(SourceItemStatus.Unmodified, sourceItem.LocalItemStatus);

            TfsFolderInfo folderInfo = engine.TfsState.GetFolderInfo(@"C:\Foo\folder");
            Assert.Equal("http://tfsUrl", folderInfo.TfsUrl);
            Assert.Equal("$/Foo/folder/", folderInfo.ServerPath);
        }

        [Fact]
        public void NonRecursive()
        {
            TestableTfsEngine engine = TestableTfsEngine.Create();

            engine.Checkout("http://tfsUrl", "$/Foo", @"C:\Foo", false, null, null);

            Assert.Equal(RecursionType.OneLevel, engine.SourceControlService.QueryItems_Recursion);
        }

        [Fact]
        public void NullLocalPath()
        {
            TestableTfsEngine engine = TestableTfsEngine.Create();

            Assert.Throws<ArgumentNullException>(delegate
                                                 {
                                                     engine.Checkout("http://tfsUrl", "$/Foo", null, true, null, null);
                                                 });
        }

        [Fact]
        public void NullServerPath()
        {
            TestableTfsEngine engine = TestableTfsEngine.Create();

            Assert.Throws<ArgumentNullException>(delegate
                                                 {
                                                     engine.Checkout("http://tfsUrl", null, @"C:\Foo", true, null, null);
                                                 });
        }

        [Fact]
        public void NullTfsUrl()
        {
            TestableTfsEngine engine = TestableTfsEngine.Create();

            Assert.Throws<ArgumentNullException>(delegate
                                                 {
                                                     engine.Checkout(null, "$/Foo", @"C:\Foo", true, null, null);
                                                 });
        }

        [Fact]
        public void NullVersionSpecGetsLatest()
        {
            TestableTfsEngine engine = TestableTfsEngine.Create();

            engine.Checkout("http://tfsUrl", "$/Foo", @"C:\Foo", true, null, null);

            Assert.Equal<VersionSpec>(VersionSpec.Latest, engine.SourceControlService.QueryItems_VersionSpec);
        }

        [Fact]
        public void RootFolderIsTracked()
        {
            TestableTfsEngine engine = TestableTfsEngine.Create();

            engine.Checkout("http://tfsUrl", "$/Foo", @"C:\Foo", true, null, null);

            Assert.True(engine.FileSystem.DirectoryExists(@"C:\Foo"));
            Assert.True(engine.TfsState.IsFolderTracked(@"C:\Foo"));

            SourceItem sourceItem = engine.TfsState.GetSourceItem(@"C:\Foo");
            Assert.Equal(@"C:\Foo", sourceItem.LocalName);
            Assert.Equal(SourceItemStatus.Unmodified, sourceItem.LocalItemStatus);
            Assert.Equal(Constants.NullItemId, sourceItem.ItemId);
            Assert.Equal(Constants.NullChangesetId, sourceItem.LocalChangesetId);
            Assert.Equal(ItemType.Folder, sourceItem.ItemType);

            TfsFolderInfo folderInfo = engine.TfsState.GetFolderInfo(@"C:\Foo");
            Assert.Equal("http://tfsUrl", folderInfo.TfsUrl);
            Assert.Equal("$/Foo/", folderInfo.ServerPath);
        }

        [Fact]
        public void WillNotCheckoutToVersionedRootFolder()
        {
            SourceItem callbackItem = null;
            SourceItemResult callbackResult = SourceItemResult.S_Ok;
            TestableTfsEngine engine = TestableTfsEngine.Create();
            engine.FileSystem.EnsurePath(@"C:\Foo");
            engine.TfsState.TrackFolder("tfsUrl2", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);

            engine.Checkout("http://tfsUrl", "$/Foo", @"C:\Foo", true, VersionSpec.Latest,
                            delegate(SourceItem item,
                                     SourceItemResult result)
                            {
                                Assert.Null(callbackItem);
                                callbackItem = item;
                                callbackResult = result;
                            });

            Assert.NotNull(callbackItem);
            Assert.Equal(ItemType.Folder, callbackItem.ItemType);
            Assert.Equal(@"C:\Foo", callbackItem.LocalName);
            Assert.Equal(callbackResult, SourceItemResult.E_AlreadyUnderSourceControl);
        }

        [Fact]
        public void WillNotCheckoutToVersionedSubFolder()
        {
            SourceItem callbackItem = null;
            SourceItemResult callbackResult = SourceItemResult.S_Ok;
            TestableTfsEngine engine = TestableTfsEngine.Create();
            engine.FileSystem.EnsurePath(@"C:\Foo");
            engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
            engine.TfsState.TrackFolder("tfsUrl2", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
            engine.TfsState.TrackFolder("tfsUrl2", "$/Foo/Bar", @"C:\Foo\Bar", 2, 1, SourceItemStatus.Unmodified);

            engine.Checkout("http://tfsUrl", "$/Foo/Bar", @"C:\Foo\Bar", true, VersionSpec.Latest,
                            delegate(SourceItem item,
                                     SourceItemResult result)
                            {
                                Assert.Null(callbackItem);
                                callbackItem = item;
                                callbackResult = result;
                            });

            Assert.NotNull(callbackItem);
            Assert.Equal(ItemType.Folder, callbackItem.ItemType);
            Assert.Equal(@"C:\Foo\Bar", callbackItem.LocalName);
            Assert.Equal(callbackResult, SourceItemResult.E_AlreadyUnderSourceControl);
        }

        [Fact]
        public void WontClobberLocalFile()
        {
            SourceItem callbackItem = null;
            SourceItemResult callbackResult = SourceItemResult.S_Ok;
            TestableTfsEngine engine = TestableTfsEngine.Create();
            engine.FileSystem.EnsurePath(@"C:\Foo");
            engine.FileSystem.WriteAllText(@"C:\Foo\file1.txt", "existing file");

            engine.Checkout("http://tfsUrl", "$/Foo", @"C:\Foo", true, null,
                            delegate(SourceItem item,
                                     SourceItemResult result)
                            {
                                if (item.LocalName == @"C:\Foo\file1.txt")
                                {
                                    callbackItem = item;
                                    callbackResult = result;
                                }
                            });

            // Local item data
            Assert.NotNull(callbackItem);
            Assert.Equal(ItemType.File, callbackItem.ItemType);
            Assert.Equal(engine.SourceControlService._SingleFile[1].ItemId, callbackItem.ItemId);
            Assert.Equal(@"C:\Foo\file1.txt", callbackItem.LocalName);
            Assert.Equal(Constants.NullChangesetId, callbackItem.LocalChangesetId);
            Assert.Equal(SourceItemStatus.Unversioned, callbackItem.LocalItemStatus);
            Assert.Equal(SourceItemResult.E_WontClobberLocalItem, callbackResult);
            // Remote item data
            Assert.Equal(engine.SourceControlService._SingleFile[1].RemoteItemStatus, callbackItem.RemoteItemStatus);
            Assert.Equal(engine.SourceControlService._SingleFile[1].RemoteChangesetId, callbackItem.RemoteChangesetId);
            Assert.Equal(engine.SourceControlService._SingleFile[1].RemoteName, callbackItem.RemoteName);
            // Data from TfsStore
            SourceItem localItem = engine.GetSourceItem(callbackItem.LocalName);
            Assert.Equal(SourceItemStatus.Unversioned, localItem.LocalItemStatus);
        }
    }
}